#include "const/oPreDefine.h"
#include "physics/oBodyDef.h"
#include "physics/oBody.h"
#include "physics/oWorld.h"
#include "physics/oSensor.h"

oBodyDef::oBodyDef()
{ }

oBodyDef::~oBodyDef()
{
	oBodyDef::clearFixtures();
}

b2FixtureDef* oBodyDef::attachPolygon( const b2Vec2& center, float width, float height, float angle, float density, float friction, float restitution )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygon(center, width, height, angle, density, friction, restitution);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachPolygon( float width, float height, float density, float friction, float restitution )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygon(width, height, density, friction, restitution);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachPolygon( const vector<b2Vec2>& vertices, float density, float friction, float restitution )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygon(vertices, density, friction, restitution);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachLoop( const vector<b2Vec2>& vertices, float friction, float restitution )
{
	b2FixtureDef* fixtureDef = oBodyDef::loop(vertices, friction, restitution);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachCircle( const b2Vec2& center, float radius, float density, float friction, float restitution )
{
	b2FixtureDef* fixtureDef = oBodyDef::circle(center, radius, density, friction, restitution);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachCircle( float radius, float density, float friction, float restitution )
{
	return oBodyDef::attachCircle(b2Vec2(0,0), radius, density, friction, restitution);
}

b2FixtureDef* oBodyDef::attachChain( const vector<b2Vec2>& vertices, float friction, float restitution )
{
	b2FixtureDef* fixtureDef = oBodyDef::chain(vertices, friction, restitution);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachPolygonSensor( uint32 tag, float width, float height )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygonSensor(tag, width, height);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachPolygonSensor( uint32 tag, float width, float height, const b2Vec2& center, float angle )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygonSensor(tag, width, height, center, angle);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachPolygonSensor( uint32 tag, const vector<b2Vec2>& vertices )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygonSensor(tag, vertices);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachCircleSensor( uint32 tag, const b2Vec2& center, float radius )
{
	b2FixtureDef* fixtureDef = oBodyDef::circleSensor(tag, center, radius);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

b2FixtureDef* oBodyDef::attachCircleSensor( uint32 tag, float radius )
{
	b2FixtureDef* fixtureDef = oBodyDef::circleSensor(tag, radius);
	_fixtures.push_back(fixtureDef);
	return fixtureDef;
}

oBody* oBodyDef::toBody( oWorld* world, oGroup* group, float x, float y, float angle )
{
	oBody* body = oBody::node(world, this);
	body->setGroup(group);
	body->setPosition(x, y);
	body->setRotation(angle);
	return body;
}

oBodyDef* oBodyDef::create()
{
	oBodyDef* bodyDef = new oBodyDef();
	bodyDef->autorelease();
	return bodyDef;
}

const vector<b2FixtureDef*>& oBodyDef::getFixtures() const
{
	return _fixtures;
}

b2FixtureDef* oBodyDef::polygon( const b2Vec2& center, float width, float height, float angle, float density, float friction, float restitution )
{
	b2PolygonShape* shape = new b2PolygonShape();
	shape->SetAsBox(oWorld::b2Val(width * 0.5f),
		oWorld::b2Val(height * 0.5f),
		oWorld::b2Val(center),
		angle);
	b2FixtureDef* fixtureDef = new b2FixtureDef();
	fixtureDef->shape = shape;
	fixtureDef->density = density;
	fixtureDef->friction = friction;
	fixtureDef->restitution = restitution;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::polygon( float width, float height, float density, float friction, float restitution )
{
	b2PolygonShape* shape = new b2PolygonShape();
	shape->SetAsBox(oWorld::b2Val(width * 0.5f), oWorld::b2Val(height * 0.5f));
	b2FixtureDef* fixtureDef = new b2FixtureDef();
	fixtureDef->shape = shape;
	fixtureDef->density = density;
	fixtureDef->friction = friction;
	fixtureDef->restitution = restitution;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::polygon( const vector<b2Vec2>& vertices, float density, float friction, float restitution )
{
	b2PolygonShape* shape = new b2PolygonShape();
	int length = vertices.size();
	b2Vec2 vs[b2_maxPolygonVertices];
	for (int i = 0; i < length; i++)
	{
		vs[i] = oWorld::b2Val(vertices[i]);
	}
	shape->Set(vs, length);
	b2FixtureDef* fixtureDef = new b2FixtureDef();
	fixtureDef->shape = shape;
	fixtureDef->density = density;
	fixtureDef->friction = friction;
	fixtureDef->restitution = restitution;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::loop( const vector<b2Vec2>& vertices, float friction, float restitution )
{
	b2ChainShape* shape = new b2ChainShape();
	int length = vertices.size();
	b2Vec2* vs = new b2Vec2[length];
	for (int i = 0; i < length; i++)
	{
		vs[i] = oWorld::b2Val(vertices[i]);
	}
	shape->CreateLoop(vs, length);
	delete [] vs;
	vs = nullptr;
	b2FixtureDef* fixtureDef = new b2FixtureDef();
	fixtureDef->shape = shape;
	fixtureDef->friction = friction;
	fixtureDef->restitution = restitution;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::circle( const b2Vec2& center, float radius, float density, float friction, float restitution )
{
	b2CircleShape* shape = new b2CircleShape();
	shape->m_p = oWorld::b2Val(center);
	shape->m_radius = oWorld::b2Val(radius);
	b2FixtureDef* fixtureDef = new b2FixtureDef();
	fixtureDef->shape = shape;
	fixtureDef->density = density;
	fixtureDef->friction = friction;
	fixtureDef->restitution = restitution;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::circle( float radius, float density, float friction, float restitution )
{
	return oBodyDef::circle(b2Vec2(0,0), radius, density, friction, restitution);
}

b2FixtureDef* oBodyDef::chain( const vector<b2Vec2>& vertices, float friction, float restitution )
{
	b2ChainShape* shape = new b2ChainShape();
	int length = vertices.size();
	b2Vec2* vs = new b2Vec2[length];
	for (int i = 0; i < length; i++)
	{
		vs[i] = oWorld::b2Val(vertices[i]);
	}
	shape->CreateChain(vs, length);
	delete [] vs;
	b2FixtureDef* fixtureDef = new b2FixtureDef();
	fixtureDef->shape = shape;
	fixtureDef->friction = friction;
	fixtureDef->restitution = restitution;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::polygonSensor( uint32 tag, float width, float height )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygon(width, height, 0, 0, 0);
	fixtureDef->isSensor = true;
	fixtureDef->userData = (void*)tag;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::polygonSensor( uint32 tag, float width, float height, const b2Vec2& center, float angle )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygon(center, width, height, angle, 0, 0, 0);
	fixtureDef->isSensor = true;
	fixtureDef->userData = (void*)tag;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::polygonSensor( uint32 tag, const vector<b2Vec2>& vertices )
{
	b2FixtureDef* fixtureDef = oBodyDef::polygon(vertices, 0, 0, 0);
	fixtureDef->isSensor = true;
	fixtureDef->userData = (void*)tag;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::circleSensor( uint32 tag, const b2Vec2& center, float radius )
{
	b2FixtureDef* fixtureDef = oBodyDef::circle(center, radius, 0, 0, 0);
	fixtureDef->isSensor = true;
	fixtureDef->userData = (void*)tag;
	return fixtureDef;
}

b2FixtureDef* oBodyDef::circleSensor( uint32 tag, float radius )
{
	b2FixtureDef* fixtureDef = oBodyDef::circle(radius, 0, 0, 0);
	fixtureDef->isSensor = true;
	fixtureDef->userData = (void*)tag;
	return fixtureDef;
}

void oBodyDef::destroyFixtureDef( b2FixtureDef*& fixtureDef )
{
	delete fixtureDef->shape;
	fixtureDef->shape = nullptr;
	delete fixtureDef;
	fixtureDef = nullptr;
}

void oBodyDef::attach( b2FixtureDef* fixtureDef )
{
	_fixtures.push_back(fixtureDef);
}

void oBodyDef::clearFixtures()
{
	BOOST_FOREACH(b2FixtureDef* fixtureDef, _fixtures)
	{
		oBodyDef::destroyFixtureDef(fixtureDef);
	}
}
